#include "RenzoMustHave.h"
#include "RenzoErrorReport.h"
#include "RenzoFloat4.h"

#ifndef _RENZO_MATRIX4_
#define _RENZO_MATRIX4_

/**
 * @description
 * Matrix is stored as column major (like OpenGL). Translation vector is located at the 13th, 14th, and 15th element.
 * m[0] m[4] m[8] m[12]
 * m[1] m[5] m[9] m[13]
 * m[2] m[6] m[10] m[14]
 * m[3] m[7] m[11] m[15]
 */
namespace Renzo {
	
	class RENZODERER_API Matrix4
	{
	public:
		float m[16];
	public:
		inline Matrix4(void) {
			memset(m, 0, sizeof(float) * 16);
		}

		inline Matrix4(float _m[16]) {
			memcpy(m, _m, sizeof(float) * 16);
		}

		inline ~Matrix4(void) {

		}

		inline static Matrix4 Identity() {
			float m[16] = {
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				0, 0, 0, 1
			};
			Matrix4 identity(m);
			return identity;
		}

		inline Matrix4 operator+(const Matrix4& u) {
			Matrix4 v;
			v.m[0] = m[0] + u.m[0];
			v.m[1] = m[1] + u.m[1];
			v.m[2] = m[2] + u.m[2];
			v.m[3] = m[3] + u.m[3];
			v.m[4] = m[4] + u.m[4];
			v.m[5] = m[5] + u.m[5];
			v.m[6] = m[6] + u.m[6];
			v.m[7] = m[7] + u.m[7];
			v.m[8] = m[8] + u.m[8];
			v.m[9] = m[9] + u.m[9];
			v.m[10] = m[10] + u.m[10];
			v.m[11] = m[11] + u.m[11];
			v.m[12] = m[12] + u.m[12];
			v.m[13] = m[13] + u.m[13];
			v.m[14] = m[14] + u.m[14];
			v.m[15] = m[15] + u.m[15];
			return v;
		}

		inline Matrix4 operator-(const Matrix4& u) {
			Matrix4 v;
			v.m[0] = m[0] - u.m[0];
			v.m[1] = m[1] - u.m[1];
			v.m[2] = m[2] - u.m[2];
			v.m[3] = m[3] - u.m[3];
			v.m[4] = m[4] - u.m[4];
			v.m[5] = m[5] - u.m[5];
			v.m[6] = m[6] - u.m[6];
			v.m[7] = m[7] - u.m[7];
			v.m[8] = m[8] - u.m[8];
			v.m[9] = m[9] - u.m[9];
			v.m[10] = m[10] - u.m[10];
			v.m[11] = m[11] - u.m[11];
			v.m[12] = m[12] - u.m[12];
			v.m[13] = m[13] - u.m[13];
			v.m[14] = m[14] - u.m[14];
			v.m[15] = m[15] - u.m[15];
			return v;
		}

		inline Matrix4 operator*(const Matrix4& u) {
			Matrix4 v;
			int i, j;
			for (i = 0; i < 4; ++i) {
				for (j = 0; j < 4; ++j) {
					v.m[i*4+j] =
						m[i*4+0]*u.m[0*4+j] +
						m[i*4+1]*u.m[1*4+j] +
						m[i*4+2]*u.m[2*4+j] +
						m[i*4+3]*u.m[3*4+j];
				}
			}
			return v;
		}

		inline float& operator[](const int index) {
			rzAssert(index >= 0 && index <= 15);
			return m[index];
		}

		inline float operator[](const int index) const {
			rzAssert(index >= 0 && index <= 15);
			return m[index];
		}

		//------------------------------------------------------------
		// Matrix - vector operation
		//------------------------------------------------------------
		inline Float4 operator*(const Float4& v) {
			Float4 u;
			u.x = m[0]*v.x + m[4]*v.y + m[8]*v.z + m[12]*v.w;
			u.y = m[1]*v.x + m[5]*v.y + m[9]*v.z + m[13]*v.w;
			u.z = m[2]*v.x + m[6]*v.y + m[10]*v.z + m[14]*v.w;
			u.w = m[3]*v.x + m[7]*v.y + m[11]*v.z + m[15]*v.w;
			return u;
		}
	};
}

#endif
